package cn.tedu.store.service.impl;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.tedu.store.config.minio.MinioConstant;
import cn.tedu.store.config.minio.service.MinioTemplate;
import cn.tedu.store.controller.ex.FileSizeException;
import cn.tedu.store.entity.Qrcode;
import cn.tedu.store.entity.dto.CodeParamDto;
import cn.tedu.store.entity.dto.TokenInfo;
import cn.tedu.store.mapper.QrcodeMapper;
import cn.tedu.store.service.QrcodeService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Service
public class QrcodeServiceImpl implements QrcodeService {

    @Resource
    MinioTemplate minioTemplate;

    @Autowired
    QrcodeMapper qrcodeMapper;

    private final Map<String, TokenInfo> tokenMap = new ConcurrentHashMap<>(8);

    /**
     * 请求微信小程序获取二维码的api地址
     */
    private String WECHAT_ACODEUN_LIMIT_URL = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=%s";
    /**
     * 获取请求token的url
     */
    private String WECHAT_BASE_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";

    @Override
    public Object createQrcode(CodeParamDto codeParam) {
        // 获取token
        String accessToken = getWxAccessToken();
        // 创建二维码
        // 构建请求参数
        String scene = codeParam.getDeptId() + "@" + codeParam.getType();
        Map<String, Object> params = MapUtil.<String, Object>builder()
                // 微信扫码后在小程序端会获取到的参数（最大32个字符）通过@拼接，小程序端自己去分割
                .put("scene", scene)
                // 跳转的小程序地址，需要小程序端提供(注意:只有正式上线的小程序地址才有效)
                .put("page", "pages/wxsq/wxsq")
                .build();
        // 请求微信接口
        String codeUrl = String.format(WECHAT_ACODEUN_LIMIT_URL, accessToken);
        byte[] bytes = HttpUtil.createPost(codeUrl).body(JSONUtil.toJsonStr(params)).execute().bodyBytes();
        if (bytes.length < 150) {
            JSONObject jsonObject = JSONUtil.parseObj(StrUtil.str(bytes, "UTF-8"));
            throw new FileSizeException(jsonObject.getStr("errmsg"));
        }
        // 转化字节流
        InputStream is = new ByteArrayInputStream(bytes);
        String imgName = scene + ".jpeg";
        try {
            minioTemplate.putObject(MinioConstant.hospitalBucketName, imgName, is);
        } catch (Exception e) {
            log.info("上传图片失败:{}", e.getLocalizedMessage());
            throw new FileSizeException("上传图片失败");
        }
        String targetImgUrl = null;
        try {
            targetImgUrl = minioTemplate.getObjectUrl(MinioConstant.hospitalBucketName, imgName);
        } catch (Exception e) {
            log.info("上传图片失败:{}", e.getLocalizedMessage());
            throw new FileSizeException("上传图片失败");
        }
        if (StrUtil.isBlank(targetImgUrl)) {
            throw new FileSizeException("上传图片失败");
        }
        // 将图片地址存入数据库中
        Qrcode qrcode = new Qrcode(codeParam.getDeptId(), codeParam.getType(), targetImgUrl);
        return qrcodeMapper.insertQrcodeInfo(qrcode);
    }

    private String getWxAccessToken() {
        TokenInfo tokenInfo = tokenMap.getOrDefault("access_token", null);
        DateTime dateNow = DateUtil.date();
        // 没有获取过token或者token过期则创建token
        if (tokenInfo == null || dateNow.isAfter(tokenInfo.getExpirationTime())) {
            String tokenUrl = String.format(WECHAT_BASE_ACCESS_TOKEN_URL, "wxbe061859f1488b2c", "9fb219fca221c7f51eedc6d7f1dad9d4");
            String tokenStr = HttpUtil.get(tokenUrl);
            JSONObject tokenObj = JSONUtil.parseObj(tokenStr);
            // 解析获取token，有效时间7200秒
            String access_token = tokenObj.getStr("access_token");
            TokenInfo tokenInfoNew = new TokenInfo(access_token, DateUtil.offset(DateUtil.date(), DateField.SECOND, 7200));
            tokenMap.put("access_token", tokenInfoNew);
            return access_token;
        }
        return tokenInfo.getAccessToken();

    }
}
